<?php
/**
 * SocialEngine
 *
 * @category   Application_Extensions
 * @package    Credit
 * @copyright  Copyright Hire-Experts LLC
 * @license    http://www.hire-experts.com
 * @version    $Id: PayPal.php 23.01.12 15:15 taalay $
 * @author     Taalay
 */

/**
 * @category   Application_Extensions
 * @package    Credit
 * @copyright  Copyright Hire-Experts LLC
 * @license    http://www.hire-experts.com
 */

class Credit_Plugin_Gateway_PayPal extends Payment_Plugin_Gateway_PayPal
{

  /**
   * Get the gateway object
   *
   * @return Engine_Payment_Gateway
   */
  public function getGateway()
  {
    if( null === $this->_gateway ) {
      $class = 'Credit_Plugin_Payment_Gateway_PayPal';
      Engine_Loader::loadClass($class);
      $gateway = new $class(array(
        'config' => (array) $this->_gatewayInfo->config,
        'testMode' => $this->_gatewayInfo->test_mode,
        'currency' => Engine_Api::_()->getApi('settings', 'core')->getSetting('payment.currency', 'USD'),
      ));
      if( !($gateway instanceof Engine_Payment_Gateway) ) {
        throw new Engine_Exception('Plugin class not instance of Engine_Payment_Gateway');
      }
      $this->_gateway = $gateway;
    }

    return $this->_gateway;
  }

  public function createCreditTransaction( Credit_Model_Order $co,	array $params = array())
 	{
    // Check that gateways match
    if( $co->gateway_id != $this->_gatewayInfo->gateway_id ) {
      throw new Engine_Payment_Plugin_Exception('Gateways do not match');
    }

    //Get unique orders
    $orders = $co->getPendingOrders();
    if ( $orders->count() <= 0){
      throw new Engine_Payment_Plugin_Exception('CREDIT_No orders found');
    }

    /**
     *
     * Get related info
     *
     * @var $translate Zend_View
     * @var $order Payment_Model_Order
     * @var $payment Credit_Model_Payment
     * @var $api Credit_Api_Core
     */

    $translate = Zend_Registry::get('Zend_Translate');
    $currency = $this->getGateway()->getCurrency();
    $payment_requests = array();
    $payment_item = array();

    $i = 0;
 		foreach($orders as $order)
    {
      if ( null == ($payment = $order->getSource())){
        $order->onIncomplete();
        continue;
      }


      $payment_item = array_merge($payment_item, array(
        'L_PAYMENTREQUEST_'.$i.'_NAME0' => $translate->_('Buying Credit'),
        'L_PAYMENTREQUEST_'.$i.'_DESC0' => $translate->_('Buying Credit from Social Engine'),
        'L_PAYMENTREQUEST_'.$i.'_AMT0' => $payment->getPrice(),
        'L_PAYMENTREQUEST_'.$i.'_NUMBER0' => $payment->getIdentity(),
        'L_PAYMENTREQUEST_'.$i.'_QTY0' => 1,
      ));

      $seller = $this->_gatewayInfo->config;

      $payment_requests = array_merge($payment_requests, array_merge(array(
        'PAYMENTREQUEST_'.$i.'_AMT' => $payment->getPrice(),
        'PAYMENTREQUEST_'.$i.'_SELLERPAYPALACCOUNTID' => $seller,
        'PAYMENTREQUEST_'.$i.'_DESC' => $translate->_('Buying Credit'),
        'PAYMENTREQUEST_'.$i.'_PAYMENTACTION' => 'order',
        'PAYMENTREQUEST_'.$i.'_PAYMENTREQUESTID' => $order->getIdentity(),
        'PAYMENTREQUEST_'.$i.'_INVNUM' => $params['credit_order_id'],
        'PAYMENTREQUEST_'.$i.'_CURRENCYCODE' => $currency,
        'PAYMENTREQUEST_'.$i.'_NOTIFYURL' => $params['ipn_url'],
      ), $payment_item));
      $i ++;
 		}


 		$params['driverSpecificParams']['PayPal'] = $payment_requests;
    $transaction = $this->createTransaction( $params );
 		return $transaction;
 	}

  public function onCreditTransactionReturn(Credit_Model_Order $co, array $params = array())
 	{
     // Check that gateways match
     if( $co->gateway_id != $this->_gatewayInfo->gateway_id ) {
       throw new Engine_Payment_Plugin_Exception('Gateways do not match');
     }

     //Get created orders
     $orders = $co->getPendingOrders();
     if ( $orders->count() <= 0){
       throw new Engine_Payment_Plugin_Exception('CREDIT_No orders found');
     }

     /**
      * Get related info
      *
      * @var $user User_Model_User
      * @var $order Payment_Model_Order
      * @var $item Credit_Model_Payment
      * @var $my_order Credit_Model_Order
      * @var $translate Zend_Translate
      * @var $gateway Credit_Plugin_Payment_Gateway_PayPal //@todo remove for other Gateways
      */

     $statuses = array();
     $translate = Zend_Registry::get('Zend_Translate');

     $my_order = $co;

     // Check for cancel state - the user cancelled the transaction
     if( $params['state'] == 'cancel' ) {
       $this->_onFailure($orders);
       // Error
       throw new Payment_Model_Exception('Your payment has been cancelled and ' .
         'not been purchased. If this is not correct, please try again later.');
     }

     // Check params
     if ( empty($params['token']) ) {
       $this->_onFailure($orders);
       // This is a sanity error and cannot produce information a user could use
       // to correct the problem.
       throw new Payment_Model_Exception('There was an error processing your ' .
         'transaction. Please try again later.');
     }

     // Get details
     try {
       $data = $this->getService()->detailExpressCheckout($params['token']);
     } catch( Exception $e ) {
       // Cancel order and subscription?
       $this->_onFailure($orders);
       // This is a sanity error and cannot produce information a user could use
       // to correct the problem.
       throw new Payment_Model_Exception('There was an error processing your ' .
           'transaction. Please try again later.');
     }

     // Let's log it
     $this->getGateway()->getLog()->log('ExpressCheckoutDetail: '
         . print_r($data, true), Zend_Log::INFO);


     //Prepare variables
     $i = 0;
     $new_requests = array();

     foreach( $data['PAYMENTREQUEST'] as $request )
     {
       if ( null == ($order = Engine_Api::_()->getItem('payment_order', $request['PAYMENTREQUESTID'])) ) {
         $statuses[$order->source_id]['error'] =  $translate->translate('CREDIT_The order was not found. ID=%1$s.', $request['PAYMENTREQUESTID']);
         continue;
       }

       if( $order->gateway_id != $this->_gatewayInfo->gateway_id ) {
         $statuses[$order->source_id]['error'] =  $translate->translate('CREDIT_Gateway id of the order %1$s do not match.', $order->getIdentity());
         continue;
       }

       if ( null == ($item = $order->getSource())) {
         if ( $order->source_type == 'credit_order') {
           $this->_onFailure($orders);
           throw new Payment_Model_Exception('CREDIT_There was an error with commission ' .
               'transaction. Please try again later.');
         } else {
           $statuses[$order->source_id]['error'] =  $translate->translate('CREDIT_Credit of the order %1$s was not found.', $order->getIdentity());
         }

         continue;
       }

       // Check order states
       if( $order->state == 'completed') {
         return 'completed';
       }

       $new_requests = array_merge($new_requests, array(
         'PAYMENTREQUEST_'.$i.'_AMT' => $request['AMT'],
         'PAYMENTREQUEST_'.$i.'_SELLERPAYPALACCOUNTID' => $request['SELLERPAYPALACCOUNTID'],
         'PAYMENTREQUEST_'.$i.'_PAYMENTACTION' => 'sale',
         'PAYMENTREQUEST_'.$i.'_PAYMENTREQUESTID' => $order->getIdentity(),
         'PAYMENTREQUEST_'.$i.'_CURRENCYCODE' => $request['CURRENCYCODE'],
       ));
       $i++;
     }

     try{
       $rdata = $this->getService()->doExpressCheckoutPayment($params['token'], $params['PayerID'], $new_requests);
     } catch( Exception $e ) {
       // Log the error
       $this->getGateway()->getLog()->log('DoExpressCheckoutPaymentError: '
           . $e->__toString(), Zend_Log::ERR);

       // Cancel order and subscription?
       $this->_onFailure($orders);
       // This is a sanity error and cannot produce information a user could use
       // to correct the problem.
       throw new Payment_Model_Exception('There was an error processing your ' .
           'transaction. Please try again later.');
     }

 		// Let's log it
 		$this->getGateway()->getLog()->log('DoExpressCheckoutPayment: '
 				. print_r($rdata, true), Zend_Log::INFO);

     foreach( $rdata['PAYMENTINFO'] as $info )
     {
       $order = Engine_Api::_()->getItem('payment_order', $info['PAYMENTREQUESTID']);
       $item = $order->getSource();

       // Get payment state
       $paymentStatus = null;
       $orderStatus = null;

       switch( strtolower($info['PAYMENTSTATUS']) ) {
         case 'created':
         case 'pending':
           $paymentStatus = 'pending';
           $orderStatus = 'complete';
           break;

         case 'completed':
         case 'processed':
         case 'canceled_reversal': // Probably doesn't apply
           $paymentStatus = 'okay';
           $orderStatus = 'complete';
           break;

         case 'denied':
         case 'failed':
         case 'voided': // Probably doesn't apply
         case 'reversed': // Probably doesn't apply
         case 'refunded': // Probably doesn't apply
         case 'expired':  // Probably doesn't apply
         default: // No idea what's going on here
           $paymentStatus = 'failed';
           $orderStatus = 'failed'; // This should probably be 'failed'
           break;
       }

       // Update order with profile info and complete status?
       $order->state = $orderStatus;
       $order->gateway_transaction_id = $info['TRANSACTIONID'];
       $order->save();

       // Insert transaction

       /**
        * @var $transactionsTable Credit_Model_DbTable_Transactions
        */
       $real_price = 0;
       if ($item instanceof Credit_Model_Payment) {
         $real_price = (float) $item->getPrice();
       }

       $transactionsTable = Engine_Api::_()->getDbTable('transactions', 'credit');
       $db = $transactionsTable->getAdapter();
       $db->beginTransaction();

       try {
         $transactionsTable->insert(array(
           'order_id' => $order->getIdentity(),
           'user_id' => $order->user_id,
           'gateway_id' => $this->_gatewayInfo->gateway_id,
           'creation_date' => new Zend_Db_Expr('NOW()'),
           'state' => $paymentStatus,
           'gateway_transaction_id' => $info['TRANSACTIONID'],
           'gateway_correlation_id' => $rdata['CORRELATIONID'],
           'credits' => $item->getCredit(), // @todo use this or gross (-fee)?
           'price' => $real_price, // @todo use this or gross (-fee)?
           'currency' => $info['CURRENCYCODE'],
         ));

         $db->commit();
       } catch( Exception $e){
         $db->rollBack();
         throw $e;
       }

       // Check payment status
       if( $paymentStatus == 'okay' ) {

         // Update credit info
         $my_order->gateway_id = $this->_gatewayInfo->gateway_id;
         $my_order->gateway_transaction_id = $info['TRANSACTIONID'];


         // Payment success
         $my_order->onPaymentSuccess();

         // Give Credits
         /**
          * @var $api Credit_Api_Core
          */
         $credits = $item->getCredit();
         $buyer = Engine_Api::_()->getItem('user', $order->user_id);

         $api = Engine_Api::_()->credit();
         $api->buyCredits($buyer, $credits, 'Paypal');

         $statuses[$order->source_id]['status'] = 'completed';
       }
       else if( $paymentStatus == 'pending' ) {

         // Update credit info
         $my_order->gateway_id = $this->_gatewayInfo->gateway_id;
         $my_order->gateway_transaction_id = $info['TRANSACTIONID'];

         if ($my_order instanceof Credit_Model_Order ){
           $my_order->gateway_correlation_id = $rdata['CORRELATIONID'];
         }

         // Payment pending
         $my_order->onPaymentPending();

         // send notification
         if( $my_order->didStatusChange() ) {

         }

         $statuses[$order->source_id]['status'] = 'pending';
       }
       else if( $paymentStatus == 'failed' ) {
         // Cancel order and subscription?
         $order->onFailure();

         if ( $my_order instanceof Credit_Model_Order ){
           $this->_onFailure($orders);
           throw new Payment_Model_Exception('CREDIT_There was an error with commission ' .
               'transaction. Please try again later.');
         }

         $my_order->onPaymentFailure();
         // Payment failed
         $statuses[$order->source_id]['error'] = $translate->translate('Your payment could not be ' .
             'completed. Please ensure there are sufficient available funds ' .
             'in your account.');
       }
       else {
         // This is a sanity error and cannot produce information a user could use
         // to correct the problem.
         $statuses[$order->source_id]['error'] = $translate->translate('There was an error processing your ' .
             'transaction. Please try again later.');
       }
     }

     return $statuses;
 	}

  public function onIpn(Engine_Payment_Ipn $ipn)
  {
    $rawData = $ipn->getRawData();

    $ordersTable = Engine_Api::_()->getDbtable('orders', 'payment');
    $transactionsTable = Engine_Api::_()->getDbtable('transactions', 'payment');


    // Find transactions -------------------------------------------------------
    $transactionId = null;
    $parentTransactionId = null;
    $transaction = null;
    $parentTransaction = null;

    // Fetch by txn_id
    if( !empty($rawData['txn_id']) ) {
      $transaction = $transactionsTable->fetchRow(array(
        'gateway_id = ?' => $this->_gatewayInfo->gateway_id,
        'gateway_transaction_id = ?' => $rawData['txn_id'],
      ));
      $parentTransaction = $transactionsTable->fetchRow(array(
        'gateway_id = ?' => $this->_gatewayInfo->gateway_id,
        'gateway_parent_transaction_id = ?' => $rawData['txn_id'],
      ));
    }
    // Fetch by parent_txn_id
    if( !empty($rawData['parent_txn_id']) ) {
      if( !$transaction ) {
        $parentTransaction = $transactionsTable->fetchRow(array(
          'gateway_id = ?' => $this->_gatewayInfo->gateway_id,
          'gateway_parent_transaction_id = ?' => $rawData['parent_txn_id'],
        ));
      }
      if( !$parentTransaction ) {
        $parentTransaction = $transactionsTable->fetchRow(array(
          'gateway_id = ?' => $this->_gatewayInfo->gateway_id,
          'gateway_transaction_id = ?' => $rawData['parent_txn_id'],
        ));
      }
    }
    // Fetch by transaction->gateway_parent_transaction_id
    if( $transaction && !$parentTransaction &&
        !empty($transaction->gateway_parent_transaction_id) ) {
      $parentTransaction = $transactionsTable->fetchRow(array(
        'gateway_id = ?' => $this->_gatewayInfo->gateway_id,
        'gateway_parent_transaction_id = ?' => $transaction->gateway_parent_transaction_id,
      ));
    }
    // Fetch by parentTransaction->gateway_transaction_id
    if( $parentTransaction && !$transaction &&
        !empty($parentTransaction->gateway_transaction_id) ) {
      $transaction = $transactionsTable->fetchRow(array(
        'gateway_id = ?' => $this->_gatewayInfo->gateway_id,
        'gateway_parent_transaction_id = ?' => $parentTransaction->gateway_transaction_id,
      ));
    }
    // Get transaction id
    if( $transaction ) {
      $transactionId = $transaction->gateway_transaction_id;
    } else if( !empty($rawData['txn_id']) ) {
      $transactionId = $rawData['txn_id'];
    }
    // Get parent transaction id
    if( $parentTransaction ) {
      $parentTransactionId = $parentTransaction->gateway_transaction_id;
    } else if( $transaction && !empty($transaction->gateway_parent_transaction_id) ) {
      $parentTransactionId = $transaction->gateway_parent_transaction_id;
    } else if( !empty($rawData['parent_txn_id']) ) {
      $parentTransactionId = $rawData['parent_txn_id'];
    }



    // Fetch order -------------------------------------------------------------
    $order = null;

    // Transaction IPN - get order by invoice
    if( !$order && !empty($rawData['invoice']) ) {
      $order = $ordersTable->find($rawData['invoice'])->current();
    }

    // Subscription IPN - get order by recurring_payment_id
    if( !$order && !empty($rawData['recurring_payment_id']) ) {
      // Get attached order
      $order = $ordersTable->fetchRow(array(
        'gateway_id = ?' => $this->_gatewayInfo->gateway_id,
        'gateway_order_id = ?' => $rawData['recurring_payment_id'],
      ));
    }

    // Subscription IPN - get order by rp_invoice_id
    //if( !$order && !empty($rawData['rp_invoice_id']) ) {
    //
    //}

    // Transaction IPN - get order by parent_txn_id
    if( !$order && $parentTransactionId ) {
      $order = $ordersTable->fetchRow(array(
        'gateway_id = ?' => $this->_gatewayInfo->gateway_id,
        'gateway_transaction_id = ?' => $parentTransactionId,
      ));
    }

    // Transaction IPN - get order by txn_id
    if( !$order && $transactionId ) {
      $order = $ordersTable->fetchRow(array(
        'gateway_id = ?' => $this->_gatewayInfo->gateway_id,
        'gateway_transaction_id = ?' => $transactionId,
      ));
    }

    // Transaction IPN - get order through transaction
    if( !$order && !empty($transaction->order_id) ) {
      $order = $ordersTable->find($parentTransaction->order_id)->current();
    }

    // Transaction IPN - get order through parent transaction
    if( !$order && !empty($parentTransaction->order_id) ) {
      $order = $ordersTable->find($parentTransaction->order_id)->current();
    }



    // Process generic IPN data ------------------------------------------------

    // Build transaction info
    if( !empty($rawData['txn_id']) ) {
      $transactionData = array(
        'gateway_id' => $this->_gatewayInfo->gateway_id,
      );
      // Get timestamp
      if( !empty($rawData['payment_date']) ) {
        $transactionData['timestamp'] = date('Y-m-d H:i:s', strtotime($rawData['payment_date']));
      } else {
        $transactionData['timestamp'] = new Zend_Db_Expr('NOW()');
      }
      // Get amount
      if( !empty($rawData['mc_gross']) ) {
        $transactionData['amount'] = $rawData['mc_gross'];
      }
      // Get currency
      if( !empty($rawData['mc_currency']) ) {
        $transactionData['currency'] = $rawData['mc_currency'];
      }
      // Get order/user
      if( $order ) {
        $transactionData['user_id'] = $order->user_id;
        $transactionData['order_id'] = $order->order_id;
      }
      // Get transactions
      if( $transactionId ) {
        $transactionData['gateway_transaction_id'] = $transactionId;
      }
      if( $parentTransactionId ) {
        $transactionData['gateway_parent_transaction_id'] = $parentTransactionId;
      }
      // Get payment_status
      switch( $rawData['payment_status'] ) {
        case 'Canceled_Reversal': // @todo make sure this works

        case 'Completed':
        case 'Created':
        case 'Processed':
          $transactionData['type'] = 'credit_payment';
          $transactionData['state'] = 'okay';
          break;

        case 'Denied':
        case 'Expired':
        case 'Failed':
        case 'Voided':
          $transactionData['type'] = 'credit_payment';
          $transactionData['state'] = 'failed';
          break;

        case 'Pending':
          $transactionData['type'] = 'credit_payment';
          $transactionData['state'] = 'pending';
          break;

        case 'Refunded':
          $transactionData['type'] = 'refund';
          $transactionData['state'] = 'refunded';
          break;
        case 'Reversed':
          $transactionData['type'] = 'reversal';
          $transactionData['state'] = 'reversed';
          break;

        default:
          $transactionData = 'unknown';
          break;
      }

			//@todo Add transaction details to credit owners
			/**
			 * @var $payment Credit_Model_Payment
			 */
			$payment = $order->getSource();

			if ( !$payment->payment_id )
			{
				// Insert new transaction
				if( !$transaction ) {
					$transactionsTable->insert($transactionData);
				}
				// Update transaction
				else {
					unset($transactionData['timestamp']);
					$transaction->setFromArray($transactionData);
					$transaction->save();
				}
			}

      // Update parent transaction on refund?
      if( $parentTransaction && in_array($transactionData['type'], array('refund','reversal')) ) {
        $parentTransaction->state = $transactionData['state'];
        $parentTransaction->save();
      }
    }



    // Process specific IPN data -----------------------------------------------
    if( $order ) {
      // Subscription IPN
      if( $order->source_type == 'credit_payment' ) {
        $this->onCreditTransactionIpn($order, $ipn);
        return $this;
      }
      // Unknown IPN
      else {
        throw new Engine_Payment_Plugin_Exception('Unknown order type for IPN');
      }
    }
    // Missing order
    else {
      throw new Engine_Payment_Plugin_Exception('Unknown or unsupported IPN ' .
          'type, or missing transaction or order ID');
    }
  }

  public function onCreditTransactionIpn(
 			Payment_Model_Order $order,
 			Engine_Payment_Ipn $ipn)
 	{
 		// Check that gateways match
 		if( $order->gateway_id != $this->_gatewayInfo->gateway_id ) {
 			throw new Engine_Payment_Plugin_Exception('Gateways do not match');
 		}

 		// Get related info
 		$user = $order->getUser();

    /**
     * @var $my_order Credit_Model_Order
     */

    $my_order = Engine_Api::_()->getItem('credit_order', $order->gateway_order_id);

 		// Get IPN data
 		$rawData = $ipn->getRawData();

 		/**
 		 * @var $transactionTb Payment_Model_DbTable_Transactions
 		 */
 		$transactionTb = Engine_Api::_()->getDbtable('transactions', 'payment');

 		// Chargeback --------------------------------------------------------------
 		if( !empty($rawData['case_type']) && $rawData['case_type'] == 'chargeback' ) {
       $my_order->onPaymentFailure(); // or should we use pending?
 		}

 		// Transaction Type --------------------------------------------------------
 		else if( !empty($rawData['txn_type']) ) {
 			if ( $rawData['txn_type'] == 'express_checkout') {
 					// Only allowed for one-time
 						switch( $rawData['payment_status'] ) {

 							case 'Created': // Not sure about this one
                 $my_order->onPaymentSuccess();
 								break;
 							case 'Pending':
                 $my_order->onPaymentPending();
 								break;
 							case 'Completed':
 							case 'Processed':
 							case 'Canceled_Reversal': // Not sure about this one
               $my_order->onPaymentSuccess();
 								// send notification
 								if( $my_order->didStatusChange() ) {
 									Engine_Api::_()->getApi('mail', 'core')->sendSystem($user, 'credit_transaction_success', array(
 										'credit_title' => 'Buying credits',
 										'credit_description' => 'You have bought some credits from us',
 										'object_link' => 'http://' . $_SERVER['HTTP_HOST'] .
 												Zend_Controller_Front::getInstance()->getRouter()->assemble(array(), 'user_login', true),
 									));
 								}
 								break;

 							case 'Denied':
 							case 'Failed':
 							case 'Voided':
 							case 'Reversed':
               $my_order->onPaymentFailure();
 								// send notification
 								if( $my_order->didStatusChange() ) {
 									Engine_Api::_()->getApi('mail', 'core')->sendSystem($user, 'credit_transaction_overdue', array(
 										'credit_title' => 'Buying credits',
 										'credit_description' => 'Error',
 										'object_link' => 'http://' . $_SERVER['HTTP_HOST'] .
 												Zend_Controller_Front::getInstance()->getRouter()->assemble(array(), 'user_login', true),
 									));
 								}
 								break;

 							case 'Refunded':
 							case 'Expired': // Not sure about this one
 								break;

 							default:
 								throw new Engine_Payment_Plugin_Exception(sprintf('Unknown IPN ' .
 										'payment status %1$s', $rawData['payment_status']));
 								break;
 						}
 			}
 			else {
 				throw new Engine_Payment_Plugin_Exception(sprintf('Unknown IPN ' .
 						'type %1$s', $rawData['txn_type']));
 			}
 		}

 		// Payment Status ----------------------------------------------------------
 		else if( !empty($rawData['payment_status']) ) {
 			switch( $rawData['payment_status'] ) {

 				case 'Created': // Not sure about this one
           $my_order->onPaymentSuccess();
 					break;
 				case 'Pending':
           $my_order->onPaymentPending();
 					break;
 				case 'Completed':
 				case 'Processed':
 				case 'Canceled_Reversal': // Not sure about this one
         $my_order->onPaymentSuccess();
 					// send notification
 					if( $my_order->didStatusChange() ) {
 						Engine_Api::_()->getApi('mail', 'core')->sendSystem($user, 'credit_transaction_success', array(
 							'credit_title' => 'Buying credits',
 							'credit_description' => 'You have bought some credits from us',
 							'object_link' => 'http://' . $_SERVER['HTTP_HOST'] .
 									Zend_Controller_Front::getInstance()->getRouter()->assemble(array(), 'user_login', true),
 						));
 					}
 					break;

 				case 'Denied':
 				case 'Failed':
 				case 'Voided':
 				case 'Reversed':
         $my_order->onPaymentFailure();
 					// send notification
 					if( $my_order->didStatusChange() ) {
 						Engine_Api::_()->getApi('mail', 'core')->sendSystem($user, 'credit_transaction_overdue', array(
 							'credit_title' => 'Buying credits',
 							'credit_description' => 'You have bought some credits from us',
 							'object_link' => 'http://' . $_SERVER['HTTP_HOST'] .
 									Zend_Controller_Front::getInstance()->getRouter()->assemble(array(), 'user_login', true),
 						));
 					}
 					break;

 				case 'Refunded':
 				case 'Expired': // Not sure about this one
 					break;

 				default:
 					throw new Engine_Payment_Plugin_Exception(sprintf('Unknown IPN ' .
 							'payment status %1$s', $rawData['payment_status']));
 					break;
 			}
 		}

 		// Unknown -----------------------------------------------------------------
 		else {
 			throw new Engine_Payment_Plugin_Exception(sprintf('Unknown IPN ' .
 					'data structure'));
 		}

 		return $this;
 	}

  protected function _paymentStatus( $status ){
		// Get payment state
		$paymentStatus = null;
		$orderStatus = null;

		switch( strtolower($status) ) {
			case 'created':
			case 'pending':
				$paymentStatus = 'pending';
				$orderStatus = 'complete';
				break;

			case 'success':
			case 'completed':
			case 'processing':
				$paymentStatus = 'okay';
				$orderStatus = 'complete';
				break;

			case 'denied':
			case 'failed':
			case 'voided': // Probably doesn't apply
			case 'reversed': // Probably doesn't apply
			case 'partially_refunded': // Probably doesn't apply
			case 'refunded': // Probably doesn't apply
			case 'expired':  // Probably doesn't apply
			default: // No idea what's going on here
				$paymentStatus = 'failed';
				$orderStatus = 'failed'; // This should probably be 'failed'
				break;
		}

    return array('paymentStatus'=>$paymentStatus, 'orderStatus'=>$orderStatus);
  }

  protected function _onFailure( $orders )
  {
    /**
     * @var $order Payment_Model_Order
     * @var $my_order Credit_Model_Order
     */

    foreach( $orders as $order )
    {
      if( !($order instanceof Payment_Model_Order) || $order->gateway_id != $this->_gatewayInfo->gateway_id ) continue;
      $order->onCancel();

      if ( null == ($payment = $order->getSource())) continue;
      $my_order = Engine_Api::_()->getItem('credit_order', $order->gateway_order_id);
      $my_order->onPaymentFailure();
    }
  }
}